//
// Created by YueChuan on 2023/5/4.
//

#ifndef YUECHUAN_HTTP_CONNECTION_H
#define YUECHUAN_HTTP_CONNECTION_H

#include "socket_stream.h"
#include "http.h"
#include "http_parser.h"
#include "uri.h"

namespace yuechuan {
    namespace http {
        /**
       * @brief Http响应结果类
       */
        class HttpResult {
        public:
            typedef std::shared_ptr<HttpResult> ptr;
            enum Error {
                /// 正常
                OK = 0,
                /// 非法的URL
                INVALID_URL = 1,
                /// 无法解析HOST
                INVALID_HOST = 2,
                /// 连接失败
                CONNECT_FAIL = 3,
                /// 连接被对端关闭
                SEND_CLOSE_BT_PEER = 4,
                /// 发送请求产生socket错误
                SEND_SOCKET_ERROR = 5,
                /// 超时
                TIMEOUT = 6,
                /// 创建socket失败
                CREATE_SOCKET_ERROR = 7,
                /// 从连接池中取连接失败
                POOL_GET_CONNECTION = 8,
                /// 无效的连接
                POOL_INVALID_CONNECTION = 9
            };

            /**
             * @brief 含参构造函数
             * @param _result 错误码
             * @param _response HTTP响应结构体
             * @param _error 错误描述
             */
            HttpResult(int _result, HttpResponse::ptr _response, const std::string &_error) :
                    result(_result), response(_response), error(_error) {}

            /// 错误码
            int result;
            /// Http响应结构体
            HttpResponse::ptr response;
            /// 错误信息
            std::string error;

            std::string toString() const;
        };

        class HttpConnection : public SocketStream {
            friend class HttpConnectionPool;
        public:
            typedef std::shared_ptr<HttpConnection> ptr;

            /**
             * @brief 含参构造函数
             * @param sock Socket类型
             * @param owner 是否托管
             */
            HttpConnection(Socket::ptr sock, bool owner = true);


            /**
             * @brief 发送HttpGET请求
             * @param uri 地址
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr 请求结果对象指针
             */
            static HttpResult::ptr DoGet(const std::string &uri, uint64_t timeout_ms,
                                         const std::map<std::string, std::string> &headers = {},
                                         const std::string &body = "");

            /**
             * @brief 发送HttpGET请求
             * @param uri Uri对象智能指针
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr 请求结果对象指针
             */
            static HttpResult::ptr DoGet(Uri::ptr uri, uint64_t timeout_ms,
                                         const std::map<std::string, std::string> &headers = {},
                                         const std::string &body = "");


            /**
             * @brief 发送HttpPOST请求
             * @param uri 地址
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr 请求结果对象指针
             */
            static HttpResult::ptr DoPost(const std::string &uri, uint64_t timeout_ms,
                                          const std::map<std::string, std::string> &headers = {},
                                          const std::string &body = "");

            /**
            * @brief 发送HttpGET请求
            * @param uri Uri对象智能指针
            * @param timeout_ms 超时时间
            * @param headers 请求头
            * @param body 请求体
            * @return HttpResult::ptr 请求结果对象指针
            */
            static HttpResult::ptr DoPost(Uri::ptr uri, uint64_t timeout_ms,
                                          const std::map<std::string, std::string> &headers = {},
                                          const std::string &body = "");

            /**
             * @brief 发送Http请求
             * @param method 指定HTTP请求的请求方法
             * @param uri 地址字符串
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr Http请求结果对象指针
             */
            static HttpResult::ptr DoRequest(HttpMethod method, const std::string &uri, uint64_t timeout_ms,
                                             const std::map<std::string, std::string> &headers = {},
                                             const std::string &body = "");

            /**
             * @brief 发送Http请求
             * @param method 指定Http请求方法
             * @param uri Uri::ptr指针duxiiang
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr Http请求结果的指针
             */
            static HttpResult::ptr DoRequest(HttpMethod method, Uri::ptr &uri, uint64_t timeout_ms,
                                             const std::map<std::string, std::string> &headers = {},
                                             const std::string &body = "");

            /**
             * @brief 发送Http请求
             * @param method 指定Http请求的方法
             * @param uri 地址字符串
             * @param timeout_ms 超时时间
             * @return HttpResult::ptr Http请求结果的指针
             */
            static HttpResult::ptr DoRequest(HttpRequest::ptr req, Uri::ptr uri, uint64_t timeout_ms);

            /**
             * @brief 接收Http请求
             * @return
             */
            HttpResponse::ptr recvResponse();

            /**
             * @brief 发送Http响应
             * @param rsp HTTP响应
             * @return > 0 成功发送
             *          = 0 对方关闭
             *          < 0 Socket异常
             */
            int sendRequest(HttpRequest::ptr req);

        private:
            uint64_t m_createTime = 0;
            uint64_t m_request = 0;
        };


        /**
         * @brief 连接池，方便进行长连接
         * @details 一个链接池只针对一个Host而言
         */
        class HttpConnectionPool {
        public:
            typedef std::shared_ptr<HttpConnectionPool> ptr;
            typedef Mutex MutexType;

            static HttpConnectionPool::ptr Create(const std::string &uri, const std::string &vhost,
                                                  uint32_t max_size, uint32_t max_alive_time,
                                                  uint32_t max_request);

            HttpConnectionPool(const std::string &host, const std::string &vhost, uint32_t port,
                               bool is_https, uint32_t max_size,
                               uint32_t max_alive_time, uint32_t max_request);

            HttpConnection::ptr getConnection();

            /**
             * @brief 发送Http的GET请求
             * @param _uri 请求的uri
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr
             */
            HttpResult::ptr doGet(const std::string &_uri,
                                  uint64_t timeout_ms,
                                  const std::map<std::string, std::string> &headers = {},
                                  const std::string &body = "");

            /**
             * @brief 发送Http的GET请求
             * @param _uri 请求的Uri::ptr 地址对象的指针
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr
             */
            HttpResult::ptr doGet(Uri::ptr &_uri,
                                  uint64_t timeout_ms,
                                  const std::map<std::string, std::string> &headers = {},
                                  const std::string &body = "");

            /**
             * @brief 发送Http的Post请求
             * @param _uri 请求的uri
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr
             */
            HttpResult::ptr doPost(const std::string &_uri,
                                   uint64_t timeout_ms,
                                   const std::map<std::string, std::string> &headers = {},
                                   const std::string &body = "");

            /**
             * @brief 发送Http的Post请求
             * @param _uri 请求的Uri::ptr 地址对象的指针
             * @param timeout_ms 超时时间
             * @param headers 请求头
             * @param body 请求体
             * @return HttpResult::ptr
             */
            HttpResult::ptr doPost(Uri::ptr &_uri,
                                   uint64_t timeout_ms,
                                   const std::map<std::string, std::string> &headers = {},
                                   const std::string &body = "");

            /**
            * @brief 发送HTTP请求
            * @param method 请求类型
            * @param uri 请求的url
            * @param timeout_ms 超时时间(毫秒)
            * @param headers HTTP请求头部参数
            * @param body 请求消息体
            * @return HttpResult::ptr
            */
            HttpResult::ptr doRequest(HttpMethod method,
                                      const std::string &url,
                                      uint64_t timeout_ms,
                                      const std::map<std::string, std::string> &headers = {},
                                      const std::string &body = "");

            /**
             * @brief 发送HTTP请求
             * @param method 请求类型
             * @param uri URI结构体
             * @param timeout_ms 超时时间(毫秒)
             * @param headers HTTP请求头部参数
             * @param body 请求消息体
             * @return HttpResult::ptr
             */
            HttpResult::ptr doRequest(HttpMethod method,
                                      Uri::ptr uri,
                                      uint64_t timeout_ms,
                                      const std::map<std::string, std::string> &headers = {},
                                      const std::string &body = "");


            HttpResult::ptr doRequest(HttpRequest::ptr req,
                                      uint64_t timeout_ms);

        private:
            static void ReleasePtr(HttpConnection *ptr, HttpConnectionPool *pool);

        private:
            /// 主机
            std::string m_host;
            /// 虚拟主机
            std::string m_vhost;
            /// 端口
            uint32_t m_port;
            /// 最多存放链接数
            uint32_t m_maxSize;
            /// 连接存活时间
            uint32_t m_maxAliveTime;
            /// 某个时间内最多请求的次数
            uint32_t m_maxRequest;
            /// 是否是https
            bool m_isHttps;

            MutexType m_mutex;
            /// 连接池队列
            std::list<HttpConnection *> m_conns;
            /// 连接池内连接的个数
            std::atomic<int32_t> m_total = {0};
        };
    }

}


#endif //YUECHUAN_HTTP_CONNECTION_H
